print('==='*30)

"""
  真实开发,面对一个项目是很难在最初就知道哪个模型更适合这个项目,所以一般是都过一遍模型.因为模型总量并不多,用已有的模型先做一个 baseline 版本的
"""

#加载飞桨和相关类库
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Linear
import numpy as np
import os
from PIL import Image

#.使用房间预测的模型
# > 模型设计之网络结构
#@ fluid.dygraph.Layer是飞桨api: 基于OOD是实现的动图层.内置多个方法函数,在训练配置会使用到,但这里没有声明.
class MNIST(fluid.dygraph.Layer):
    def __init__(self):
        super(MNIST, self).__init__()
        
        #> 定义一层全连接层，input输入784的数据量, 输出维度是1(784放入一个数组中)，激活函数为None，即不使用激活函数
        #@ Linear 线性函数
        self.fc = Linear(input_dim=784, output_dim=1, act=None)
        
    #> forward函数中将网络串联:  定义网络结构的前向计算过程
    #@ self MNIST自己
    #@ inputs 输入数据
    def forward(self, inputs):
       
        #> 模型自己,通过的fc方式将输入的数据,转换成一维数据
        outputs = self.fc(inputs)
       
        #> 返回转换成一维的数据
        return outputs

#. 训练配置
#> 定义飞桨动态图工作环境
with fluid.dygraph.guard():
    #> 声明设计好的模型
    model = MNIST()

    #> 启动训练模式
    #@ train()是fluid.dygraph.Layer内置方法 :将此层及其所有子层设置为训练模式
    model.train()

    #> 定义数据读取函数，数据读取batch_size设置为16
    #@  paddle.dataset.mnist.train()是飞桨集成好的训练数据集,
    #@   paddle.batch,将获得的数据, 分为16个数组做为第一层数据
    train_loader = paddle.batch(paddle.dataset.mnist.train(), batch_size=16)
    
    #> 定义优化器，使用随机梯度下降SGD优化器，学习率设置为0.001
    #! 梯度下降跳过去了,需要回头在去看看 
    optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001, 
    
    #> 回一个由当前层及其子层的所有参数组成的列表
    #@ parameters : 返回一个由当前层及其子层的所有参数组成的列表。
    parameter_list=model.parameters())

#. 训练过程
#> 通过with语句创建一个调整的运行环境的机会


with fluid.dygraph.guard():
    #> 训练配置的代码一致,这里不再填写注释
    model = MNIST()
    model.train()
    train_loader = paddle.batch(paddle.dataset.mnist.train(), batch_size=16)
    optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001, parameter_list=model.parameters())
    
    #> 设定商量值, 一般代码中不会写魔术数字.数字均有常量来代替并注释说明 
    EPOCH_NUM = 10
    #> 循环 EPOCH_NUM 常量的次数 = 10次
    for epoch_id in range(EPOCH_NUM):
        #> train_loader()获得了16*N的数据假如到第二层循环中
        for batch_id, data in enumerate(train_loader()):
            #> 准备数据，格式需要转换成符合框架要求
            image_data = np.array([x[0] for x in data]).astype('float32')
            label_data = np.array([x[1] for x in data]).astype('float32').reshape(-1, 1)
            #> 将数据转为飞桨动态图格式
            image = fluid.dygraph.to_variable(image_data)
            label = fluid.dygraph.to_variable(label_data)
            
            #> 前向计算的过程
            predict = model(image)
            
            #>计算损失，取一个批次样本损失的平均值
            loss = fluid.layers.square_error_cost(predict, label)
            avg_loss = fluid.layers.mean(loss)
            
            #> 每训练了1000批次的数据，打印下当前Loss的情况
            if batch_id !=0 and batch_id  % 1000 == 0:
                print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
            
            #> 后向传播，更新参数的过程
            avg_loss.backward()
            optimizer.minimize(avg_loss)
            model.clear_gradients()

#. 保存模型
fluid.save_dygraph(model.state_dict(), 'mnist')


